{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7765UFHoyGx6"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "KVtTDrUNyL7x"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xPYxZMrWyA0N"
      },
      "source": [
        "# Estimators を使用するブースティング木"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "p_vOREjRx-Y0"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "<img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\"><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/tutorials/estimator/boosted_trees.ipynb\">TensorFlow.org で表示</a>\n",
        "</td>\n",
        "  <td>\n",
        "<img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\"><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ja/tutorials/estimator/boosted_trees.ipynb\">Google Colab で実行</a>\n",
        "</td>\n",
        "  <td>\n",
        "<img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\"><a target=\"_blank\" href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/tutorials/estimator/boosted_trees.ipynb\">GitHubでソースを表示</a>\n",
        "</td>\n",
        "  <td>\n",
        "<img src=\"https://www.tensorflow.org/images/download_logo_32px.png\"><a href=\"https://storage.googleapis.com/tensorflow_docs/docs/site/en/tutorials/estimator/boosted_trees.ipynb\">ノートブックをダウンロード</a>\n",
        "</td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dW3r7qVxzqN5"
      },
      "source": [
        "このチュートリアルは、`tf.estimator`API で決定木を使用する勾配ブースティングモデルのエンドツーエンドのウォークスルーです。ブースティング木モデルは、回帰と分類の両方のための最も一般的かつ効果的な機械学習アプローチの 1 つです。これは、複数（10 以上、100 以上、あるいは 1000 以上の場合も考えられます）の木モデルからの予測値を結合するアンサンブル手法です。\n",
        "\n",
        "最小限のハイパーパラメータ調整で優れたパフォーマンスを実現できるため、ブースティング木モデルは多くの機械学習実践者に人気があります。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "eylrTPAN3rJV"
      },
      "source": [
        "## Titanic データセットを読み込む\n",
        "\n",
        "Titanic データセットを使用します。ここでの目標は、性別、年齢、船室クラスなどの特徴に基づき、（やや悪趣味ではありますが）乗船者の生存を予測することです。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "KuhAiPfZ3rJW"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import pandas as pd\n",
        "from IPython.display import clear_output\n",
        "from matplotlib import pyplot as plt\n",
        "\n",
        "# Load dataset.\n",
        "dftrain = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/train.csv')\n",
        "dfeval = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/eval.csv')\n",
        "y_train = dftrain.pop('survived')\n",
        "y_eval = dfeval.pop('survived')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "NFtnFm1T0kMf"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf\n",
        "tf.random.set_seed(123)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3ioodHdVJVdA"
      },
      "source": [
        "データセットはトレーニングセットと評価セットで構成されています。\n",
        "\n",
        "- `dftrain`と`y_train`は *トレーニングセット*です — モデルが学習に使用するデータです。\n",
        "- モデルは*評価セット*、`dfeval`、`y_eval`に対してテストされます。\n",
        "\n",
        "トレーニングには以下の特徴を使用します。\n",
        "\n",
        "<table>\n",
        "  <tr>\n",
        "    <th>特徴名</th>\n",
        "    <th>説明</th>\n",
        "  </tr>\n",
        "  <tr>\n",
        "    <td>sex</td>\n",
        "    <td>乗船者の性別</td>\n",
        "  </tr>\n",
        "  <tr>\n",
        "    <td>age</td>\n",
        "    <td>乗船者の年齢</td>\n",
        "  </tr>\n",
        "    <tr>\n",
        "    <td>n_siblings_spouses</td>\n",
        "    <td>同乗する兄弟姉妹および配偶者</td>\n",
        "  </tr>\n",
        "    <tr>\n",
        "    <td>parch</td>\n",
        "    <td>同乗する両親および子供</td>\n",
        "  </tr>\n",
        "    <tr>\n",
        "    <td>fare</td>\n",
        "    <td>運賃</td>\n",
        "  </tr>\n",
        "    <tr>\n",
        "    <td>class</td>\n",
        "    <td>船室のクラス</td>\n",
        "  </tr>\n",
        "    <tr>\n",
        "    <td>deck</td>\n",
        "    <td>搭乗デッキ</td>\n",
        "  </tr>\n",
        "    <tr>\n",
        "    <td>embark_town</td>\n",
        "    <td>乗船者の乗船地</td>\n",
        "  </tr>\n",
        "    <tr>\n",
        "    <td>alone</td>\n",
        "    <td>一人旅か否か</td>\n",
        "  </tr>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AoPiWsJALr-k"
      },
      "source": [
        "## データを検証する"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "slcat1yzmzw5"
      },
      "source": [
        "まず最初に、データの一部をプレビューして、トレーニングセットの要約統計を作成します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "15PLelXBlxEW"
      },
      "outputs": [],
      "source": [
        "dftrain.head()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "j2hiM4ETmqP0"
      },
      "outputs": [],
      "source": [
        "dftrain.describe()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-IR0e8V-LyJ4"
      },
      "source": [
        "トレーニングセットと評価セットには、それぞれ 627 個と 264 個の例があります。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "_1NwYqGwDjFf"
      },
      "outputs": [],
      "source": [
        "dftrain.shape[0], dfeval.shape[0]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "28UFJ4KSMK3V"
      },
      "source": [
        "乗船者の大半は 20 代から 30 代です。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "CaVDmZtuDfux"
      },
      "outputs": [],
      "source": [
        "dftrain.age.hist(bins=20)\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1pifWiCoMbR5"
      },
      "source": [
        "男性の乗船者数は女性の乗船者数の約 2 倍です。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "-WazAq30MO5J"
      },
      "outputs": [],
      "source": [
        "dftrain.sex.value_counts().plot(kind='barh')\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7_XkxrpmmVU_"
      },
      "source": [
        "乗船者の大半は「3 等」の船室クラスを利用していました。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "zZ3PvVy4l4gI"
      },
      "outputs": [],
      "source": [
        "dftrain['class'].value_counts().plot(kind='barh')\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HM5SlwlxmZMT"
      },
      "source": [
        "大半の乗船者はサウサンプトンから乗船しています。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "RVTSrdr4mZaC"
      },
      "outputs": [],
      "source": [
        "dftrain['embark_town'].value_counts().plot(kind='barh')\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "aTn1niLPob3x"
      },
      "source": [
        "女性は男性よりも生存する確率がはるかに高く、これは明らかにモデルの予測特徴です。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Eh3KW5oYkaNS"
      },
      "outputs": [],
      "source": [
        "pd.concat([dftrain, y_train], axis=1).groupby('sex').survived.mean().plot(kind='barh').set_xlabel('% survive')\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "krkRHuMp3rJn"
      },
      "source": [
        "## 特徴量カラムを作成して関数を入力する\n",
        "\n",
        "勾配ブースティング Estimator は数値特徴とカテゴリ特徴の両方を利用します。特徴量カラムは、全ての TensorFlow Estimator と機能し、その目的はモデリングに使用される特徴を定義することにあります。さらに、One-Hot エンコーディング、正規化、バケット化などいくつかの特徴量エンジニアリング機能を提供します。このチュートリアルでは、`CATEGORICAL_COLUMNS`のフィールドはカテゴリカラムから One-Hot エンコーディングされたカラム（[インジケータカラム](https://www.tensorflow.org/api_docs/python/tf/feature_column/indicator_column)）に変換されます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "upaNWxcF3rJn"
      },
      "outputs": [],
      "source": [
        "CATEGORICAL_COLUMNS = ['sex', 'n_siblings_spouses', 'parch', 'class', 'deck',\n",
        "                       'embark_town', 'alone']\n",
        "NUMERIC_COLUMNS = ['age', 'fare']\n",
        "\n",
        "def one_hot_cat_column(feature_name, vocab):\n",
        "  return tf.feature_column.indicator_column(\n",
        "      tf.feature_column.categorical_column_with_vocabulary_list(feature_name,\n",
        "                                                 vocab))\n",
        "feature_columns = []\n",
        "for feature_name in CATEGORICAL_COLUMNS:\n",
        "  # Need to one-hot encode categorical features.\n",
        "  vocabulary = dftrain[feature_name].unique()\n",
        "  feature_columns.append(one_hot_cat_column(feature_name, vocabulary))\n",
        "\n",
        "for feature_name in NUMERIC_COLUMNS:\n",
        "  feature_columns.append(tf.feature_column.numeric_column(feature_name,\n",
        "                                           dtype=tf.float32))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "74GNtFpStSAz"
      },
      "source": [
        "特徴量カラムが生成する変換は表示することができます。例えば、`indicator_column`を単一の例で使用した場合の出力は次のようになります。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Eaq79D9FtmF8"
      },
      "outputs": [],
      "source": [
        "example = dict(dftrain.head(1))\n",
        "class_fc = tf.feature_column.indicator_column(tf.feature_column.categorical_column_with_vocabulary_list('class', ('First', 'Second', 'Third')))\n",
        "print('Feature value: \"{}\"'.format(example['class'].iloc[0]))\n",
        "print('One-hot encoded: ', tf.keras.layers.DenseFeatures([class_fc])(example).numpy())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YbCUn3nCusC3"
      },
      "source": [
        "さらに、特徴量カラムの変換を全てまとめて表示することができます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "omIYcsVws3g0"
      },
      "outputs": [],
      "source": [
        "tf.keras.layers.DenseFeatures(feature_columns)(example).numpy()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-UOlROp33rJo"
      },
      "source": [
        "次に、入力関数を作成する必要があります。これらはトレーニングと推論の両方のためにデータをモデルに読み込む方法を指定します。[ `tf.data`](https://www.tensorflow.org/api_docs/python/tf/data) API の`from_tensor_slices`メソッドを使用して Pandas から直接データを読み取ります。これは小規模でインメモリのデータセットに適しています。大規模のデータセットの場合は、多様なファイル形式（[csv](https://www.tensorflow.org/api_docs/python/tf/data/experimental/make_csv_dataset)を含む）をサポートする tf.data API を使用すると、メモリに収まりきれないデータセットも処理することができます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "9dquwCQB3rJp"
      },
      "outputs": [],
      "source": [
        "# Use entire batch since this is such a small dataset.\n",
        "NUM_EXAMPLES = len(y_train)\n",
        "\n",
        "def make_input_fn(X, y, n_epochs=None, shuffle=True):\n",
        "  def input_fn():\n",
        "    dataset = tf.data.Dataset.from_tensor_slices((dict(X), y))\n",
        "    if shuffle:\n",
        "      dataset = dataset.shuffle(NUM_EXAMPLES)\n",
        "    # For training, cycle thru dataset as many times as need (n_epochs=None).\n",
        "    dataset = dataset.repeat(n_epochs)\n",
        "    # In memory training doesn't use batching.\n",
        "    dataset = dataset.batch(NUM_EXAMPLES)\n",
        "    return dataset\n",
        "  return input_fn\n",
        "\n",
        "# Training and evaluation input functions.\n",
        "train_input_fn = make_input_fn(dftrain, y_train)\n",
        "eval_input_fn = make_input_fn(dfeval, y_eval, shuffle=False, n_epochs=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HttfNNlN3rJr"
      },
      "source": [
        "## モデルをトレーニングして評価する\n",
        "\n",
        "以下のステップで行います。\n",
        "\n",
        "1. 特徴とハイパーパラメータを指定してモデルを初期化する。\n",
        "2. `train_input_fn`を使用してモデルにトレーニングデータを与え、`train`関数を使用してモデルをトレーニングする。\n",
        "3. 評価セット（この例では`dfeval` DataFrame）を使用してモデルのパフォーマンスを評価する。予測値が`y_eval`配列のラベルと一致することを確認する。\n",
        "\n",
        "ブースティング木モデルをトレーニングする前に、まず線形分類器（ロジスティック回帰モデル）をトレーニングしてみましょう。ベンチマークを確立するには、より単純なモデルから始めるのがベストプラクティスです。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "JPOGpmmq3rJr"
      },
      "outputs": [],
      "source": [
        "linear_est = tf.estimator.LinearClassifier(feature_columns)\n",
        "\n",
        "# Train model.\n",
        "linear_est.train(train_input_fn, max_steps=100)\n",
        "\n",
        "# Evaluation.\n",
        "result = linear_est.evaluate(eval_input_fn)\n",
        "clear_output()\n",
        "print(pd.Series(result))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BarkNXwA3rJu"
      },
      "source": [
        "次に、ブースティング木モデルをトレーニングしてみましょう。ブースティング木では、回帰（`BoostedTreesRegressor`）と分類（`BoostedTreesClassifier`）をサポートします。目標は、生存か非生存かのクラスを予測することなので、`BoostedTreesClassifier`を使用します。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tgEzMtlw3rJu"
      },
      "outputs": [],
      "source": [
        "# Since data fits into memory, use entire dataset per layer. It will be faster.\n",
        "# Above one batch is defined as the entire dataset.\n",
        "n_batches = 1\n",
        "est = tf.estimator.BoostedTreesClassifier(feature_columns,\n",
        "                                          n_batches_per_layer=n_batches)\n",
        "\n",
        "# The model will stop training once the specified number of trees is built, not\n",
        "# based on the number of steps.\n",
        "est.train(train_input_fn, max_steps=100)\n",
        "\n",
        "# Eval.\n",
        "result = est.evaluate(eval_input_fn)\n",
        "clear_output()\n",
        "print(pd.Series(result))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hEflwznXvuMP"
      },
      "source": [
        "このトレーニングモデルを使用して、評価セットからある乗船者に予測を立てることができます。TensorFlow モデルは、バッチ、コレクション、または例に対してまとめて予測を立てられるように最適化されています。以前は、`eval_input_fn` は評価セット全体を使って定義されていました。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6zmIjTr73rJ4"
      },
      "outputs": [],
      "source": [
        "pred_dicts = list(est.predict(eval_input_fn))\n",
        "probs = pd.Series([pred['probabilities'][1] for pred in pred_dicts])\n",
        "\n",
        "probs.plot(kind='hist', bins=20, title='predicted probabilities')\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "mBUaNN1BzJHG"
      },
      "source": [
        "最後に、結果の受信者操作特性（ROC）を見てみましょう。真陽性率と偽陽性率間のトレードオフに関し、より明確な予想を得ることができます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "NzxghvVz3rJ6"
      },
      "outputs": [],
      "source": [
        "from sklearn.metrics import roc_curve\n",
        "\n",
        "fpr, tpr, _ = roc_curve(y_eval, probs)\n",
        "plt.plot(fpr, tpr)\n",
        "plt.title('ROC curve')\n",
        "plt.xlabel('false positive rate')\n",
        "plt.ylabel('true positive rate')\n",
        "plt.xlim(0,)\n",
        "plt.ylim(0,)\n",
        "plt.show()"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "boosted_trees.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
